/** Class: Adler.java
*	Author:	
*	Create Date:
*	Last Modified by: Jiaxing Zhang
*	Last Modified on: Mar. 15, 2009, 16:14:00
*	Notes:This is the root class for the Adler Data Management tool.
*		  Adler manages a database containing data about a source project, and fields
*		  requests to add data and query against the dataset.
*	Change History:
*/ 

package com.cseg674.datamanagement.adler;

import java.io.StringReader;

import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.db.RDFRDBException;

/** The implementation class for DataInserter and SparlQuerier interfaces.
 *  
 */
public class Adler implements DataInserter, SparqlQuerier
{
	/** Class constructor
	 * 
	 */
    public Adler() throws AdlerException
    {
		// Get config file to setup the database connection
        dbc = ConfigFileParser.getConfig(ADLER_CONFIG_FILE);
        // Initialize the database
        dbc.initialize();
        
        dbModelMgr = new DBModel(dbc.getConnection(),
            dbc.getDefaultProjectName(), dbc.getOntology());
    }

    /**
     * Inserts data for the specified project.
     *
     * @param project        The name of the project to insert data into. 
     * @param semanticData   The semantic data to insert into the project in
     *                       one of the formats specified by dataLanguage.
     * @param baseURI        The URI for the ontology.
     * @param dataLanguage   Format of semanticData. Supported formats:
     *                       "RDF/XML", "N-TRIPLE", "N3", and 'null'. 
     *                       'null' is equiv. to "RDF/XML".
     * @throws AdlerException
     */
    /// from DataInserter
    public void insertProject(String project, String semanticData,
        String baseURI, String dataLanguage) throws AdlerException
    {
    	if(dbModelMgr.checkIfModelExists(project)) {
    		throw new AdlerException("Project (" + project + ") already exists.");
    	}

        // create an in-memory model before writing it to the database
        Model tempModel = ModelFactory.createDefaultModel();
        
        // The Jena docs say it's bad to use the io.reader versions of this 
        // method. Mua ha ha!!!
        // fixme: add try/catch for parsing errors and return something 
        // intelligent to caller?

        //attempt to parse the source data into a Model:
        tempModel.read(new StringReader(semanticData), baseURI, dataLanguage);
        
        // create a new default model and get a reference to it
        Model persistModel = dbModelMgr.createModel(project);
        // Lock the resource
        persistModel.enterCriticalSection(Lock.WRITE);
        // here's where the magic stuff happens. Add all the statements in the
        // temporary Model to the database.
        Model updatedModel;
        try {
            updatedModel = persistModel.add(tempModel);
        } finally { 
            persistModel.leaveCriticalSection();
        } 
        if(updatedModel == null) // if adding model fails.
			{
    		throw new AdlerException("Failed to create project (" + project + ").");
        }

    }

    /**
     * Replace a specified project with all new data. If the project does not
     * exist, a new project with the provided semantic data will be created.
     *
     * @param project        The name of the project to insert data into. 
     * @param semanticData   The semantic data to insert into the project in
     *                       one of the formats specified by dataLanguage.
     * @param baseURI        The URI for the ontology.
     * @param dataLanguage   Format of semanticData. Supported formats:
     *                       "RDF/XML", "N-TRIPLE", "N3", and 'null'. 
     *                       'null' is equiv. to "RDF/XML".
     * @throws AdlerException
     */
    
    // replace an existing project
    // if project does not exist, then just insert it
    public void replaceProject(String project, String semanticData,
        String baseURI, String dataLanguage) throws AdlerException
    {
		//Check to see if the model already exists, if yes, delete it.
    	if(dbModelMgr.checkIfModelExists(project)) {
            deleteProject(project);
    	}
        //Insert new project
        insertProject(project, semanticData, baseURI, dataLanguage);
    }

    /**
     * Delete all semantic data for a particular project.
     *
     * @param  project           The name of the project to insert data into
     * @throws AdlerException
     */
    // delete an existing project
    public void deleteProject(String project) throws AdlerException {
    	if(!dbModelMgr.checkIfModelExists(project)) {
    		throw new AdlerException("Project (" + project + ") does not exist.");
    	}

        dbModelMgr.deleteModel(project);
    }
  
    /**
     * Retrieve the ontology URI for a particular project. Currently, this only
     * supports one ontology, but the project name is provided here for future
     * implementation that can support multiple ontologies.
     *
     * @param  project   name of the project whose ontology is sought
     * @return           string of the ontology URI
     */
    
    /// get the ontology URI
    // currently does not support multiple ontologies, but project name
    // is provided as a parameter in case we add this functionality. JXZ
    public String getOntologyURI(String project) throws AdlerException
		{
        if(!dbModelMgr.checkIfModelExists(project)) {
    		throw new AdlerException("Project (" + project + ") does not exist.");
    	}
		return dbc.getOntology();
    }

	   /**
	    * Returns a list of all the Java projects stored in the database with a
	    * Jena ExtendedIterator 
	    * 
	    * @return	An iterator for listing the project names
	    * @throws	AdlerException
	    */
    
    public ExtendedIterator listProjects() throws AdlerException {
		//Return the list of project names
        try{
		  return dbModelMgr.listModels();
		}
		catch(RDFRDBException e){//catch exception
		  throw new AdlerException("Database Error occured during "
            + "listProjects");
		}
    }

	   /**
	    * Runs a query against the specified project and returns the results in a 
	    * Jena ResultSet
	    * 
	    *  @param	project	The name of the project to query against
	    *  @param	query	The SPARQL query to submit
	    * 
	    * @return	A container with the results of the query
	    * @throws	AdlerException
	    */

    /// from SparqlQuerier
    public ResultSet submitQuery(String project, String strQuery)
        throws AdlerException
    {

    	if(!dbModelMgr.checkIfModelExists(project)) {
    		throw new AdlerException("Project (" + project + ") does not exist.");
    	}
        //Set up the query
        Query query = QueryFactory.create(strQuery);

        // Execute the query and obtain results
        QueryExecution qe = QueryExecutionFactory.create(query,
            dbModelMgr.getInfModel(project));
        // get result
        ResultSet results = qe.execSelect();

        // Important - free up resources used running the query
        qe.close();    

        return results;
    }
  
    //Here is the location of the ConfigFile.
    private final static String ADLER_CONFIG_FILE = "C:\\Users\\Frank\\Dev\\Workspace\\CSEG674\\WEB-INF\\config.ini";

    private DatabaseConnection dbc;
    private DBModel            dbModelMgr;
}